package com.medsoft.drpcp;

import java.io.IOException;
import java.lang.reflect.Method;
import java.util.Arrays;
import java.util.HashSet;
import java.util.List;
import java.util.Set;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.core.io.Resource;
import org.springframework.core.io.support.PathMatchingResourcePatternResolver;
import org.springframework.core.io.support.ResourcePatternResolver;
import org.springframework.core.type.classreading.CachingMetadataReaderFactory;
import org.springframework.core.type.classreading.MetadataReader;
import org.springframework.core.type.classreading.MetadataReaderFactory;
import org.springframework.core.type.filter.AnnotationTypeFilter;
import org.springframework.core.type.filter.TypeFilter;
import org.springframework.util.ClassUtils;

import com.medsoft.drpcp.annotation.Service;
import com.medsoft.drpcp.annotation.ServiceModule;
import com.medsoft.drpcp.client.RpcServiceProxyImpl;
import com.medsoft.drpcp.meta.DrpcpMetaConstant;

public class RpcClassScanner {
    private static ResourcePatternResolver resourcePatternResolver = new PathMatchingResourcePatternResolver();
	static final Logger logger = LoggerFactory.getLogger(RpcClassScanner.class);
	private static final String RESOURCE_PATTERN = "/**/*.class";  
	private static Set<Class<?>> classSet = new HashSet<Class<?>>();
	
	private static TypeFilter typeFilter = new AnnotationTypeFilter(ServiceModule.class, false,true);

	public static Set<Class<?>> parse(String serviceType,String beanPackages) {
		List<String> packagesToScan = Arrays.asList(beanPackages.split(";"));
		try {
			return getClassSet(packagesToScan);
		} catch (ClassNotFoundException | IOException e) {
			e.printStackTrace();
			throw new RuntimeException("scanning " + serviceType + " package path " + packagesToScan.toArray().toString() + " failed, please check the config.");
		}
	}

	public static void createExportService(String beanPackages) {
		logger.info("preparing to create auto export service, path " + beanPackages + "...");
		Set<Class<?>> exportClasses = parse("export service",beanPackages);
		putServiceIntoContainer(false,exportClasses);
	}

	public static void createProxyService(String beanPackages) {
		logger.info("preparing to create auto proxy service, path " + beanPackages + "...");
		Set<Class<?>> proxyClasses = parse("proxy service",beanPackages);
		for(Class serviceInterface : proxyClasses) {
			Bootstrapper.registerBean("AutoProxy." + serviceInterface.getCanonicalName(),serviceInterface, RpcServiceProxyImpl.class);
			logger.info("createed AutoProxy." + serviceInterface.getCanonicalName());
		}
		putServiceIntoContainer(false,proxyClasses);
	}
	
	@SuppressWarnings({ "unchecked", "rawtypes" })
	private static void putServiceIntoContainer(boolean isExport,
			Set<Class<?>> serviceClasses) {
		for(Class clz : serviceClasses) {
			ServiceModule moduleAnno = (ServiceModule) clz.getAnnotation(ServiceModule.class);
			for(Method method : Arrays.asList(clz.getDeclaredMethods())) {
				Service serviceAnno = method.getAnnotation(Service.class);
				if (serviceAnno != null) {
					ServiceDefinition service = new ServiceDefinition();
					service.setServiceId(serviceAnno.serviceId())
							.setDesc(serviceAnno.desc())
							.setTimeout(serviceAnno.timeout())
							.setMethod(method)
							.setClz(clz)
							.setSystemId(moduleAnno.systemId())
							.setSubSystemId(moduleAnno.subSystemId())
							.setRetType(method.getReturnType())
							.setParamType(method.getParameterTypes());
					if(isExport) {
						service.setNodeName(DrpcpMetaConstant.NODE_NAME_LOCALSERVICE);
					} else {
						if(service.getTimeout() == 0) {
							service.setTimeout(GlobalConfig.timeout);
						}
					}
					ServiceDefinitionContainer.addService(serviceAnno.serviceId(), service);
					logger.info("add drpcp service " + service.toString() + "to container.");
				}
			}
		}
	}

	/**
	 * 将符合条件的Bean以Class集合的形式返回
	 * 
	 * @return
	 * @throws IOException
	 * @throws ClassNotFoundException
	 */
	public static Set<Class<?>> getClassSet(List<String> packagesToScan) throws IOException, ClassNotFoundException {
		classSet.clear();
		if (!packagesToScan.isEmpty()) {
			for (String pkg : packagesToScan) {
				String pattern = ResourcePatternResolver.CLASSPATH_ALL_URL_PREFIX
						+ ClassUtils.convertClassNameToResourcePath(pkg)
						+ RESOURCE_PATTERN;
				Resource[] resources = resourcePatternResolver.getResources(pattern);
				MetadataReaderFactory readerFactory = new CachingMetadataReaderFactory(resourcePatternResolver);
				for (Resource resource : resources) {
					if (resource.isReadable()) {
						MetadataReader reader = readerFactory.getMetadataReader(resource);
						String className = reader.getClassMetadata().getClassName();
						if (matchesEntityTypeFilter(reader, readerFactory)) {
							classSet.add(Class.forName(className));
							logger.info("find auto proxy/export interfac: " + className);
						}
					}
				}
			}
		}
		return classSet;
	}
	
	public static void main(String[] args) {
		//ServiceModule moduleAnno = (ServiceModule) T.class.getAnnotation(ServiceModule.class).annotationType().getAnnotation(ServiceModule.class);
		/*
		List<String> pkgs = new ArrayList<String>();
		try {
			pkgs.add("com.medsoft.medical.service");
			getClassSet(pkgs);
		} catch (ClassNotFoundException | IOException e) {
			e.printStackTrace();
		}
		*/
	}
	
	private static boolean matchesEntityTypeFilter(MetadataReader reader, MetadataReaderFactory readerFactory) throws IOException {  
		if (typeFilter.match(reader, readerFactory)) {  
            return true;  
        }
        return false;  
    }
}
